DinkLua Object Reference

Introduction

This is a brief guide containing a reference of object methods in DinkLua as well as extra tables added to YeOldeDink that are not part of the base DinkLua spec. It assumes you've read the introduction from the author, and also have some existing knowledge of DinkC/engine workings, as well as some basic familiarity with object-oriented principles.

These can be troublesome as object function methods use the : semicolon operator, rather than dot as is used for table functions and therefore may be quite confusing.

The main table containing the bulk of engine features, "dink", is described separately in its own document.

Sprites

Sprites are created using the create_sprite function as part of the "dink" table. Most DinkC-equivalent features are also under this table, however in the case of sprite properties, they are altered like so:

mysprite = dink.create_sprite(100, 100, 0, 100, 1)
myothersprite = dink.create_sprite(120, 120, 6, 200, 1)
-- getting a sprite number from a DinkC script using juggle var
mythirdsprite = dink.get_sprite(global.juggle)
-- add 5 to sprite's position
mysprite.x = mysprite.x + 5
mysprite.y = 110
-- set sprite to not clip
mysprite.noclip = true
mysprite = dink.create_sprite(100, 100, 0, 100, 1)
myothersprite = dink.create_sprite(120, 120, 6, 200, 1)
-- getting a sprite number from a DinkC script using juggle var
mythirdsprite = dink.get_sprite(global.juggle)
-- add 5 to sprite's position
mysprite.x = mysprite.x + 5
mysprite.y = 110
-- set sprite to not clip
mysprite.noclip = true

The rest of the sprite properties include:

mysprite.active = false
-- used by some sprite brains
mysprite.action = 2
mysprite.attack_hit_sound = 44
mysprite.attack_hit_sound_speed = 96000
-- time in ms before the sprite runs attack()
mysprite.attack_wait = 10
-- base sequences
mysprite.base_attack = 450
mysprite.base_die = 200
mysprite.base_death = 200
mysprite.base_hit = 100
mysprite.base_idle = 10
mysprite.base_walk = 20
mysprite.brain = brain.REPEAT or 6
mysprite.brain_parm = 5
mysprite.brain_parm2 = 10
mysprite.defense = 100
mysprite.dir = direction.SOUTH or 2
mysprite.disabled = false
mysprite.distance = 100
mysprite.exp = 5000
mysprite.flying = true
mysprite.follow = myothersprite
mysprite.frame = 1
mysprite.seq = 100
mysprite.frame_delay = 33
mysprite.gold = 9000
mysprite.hard = true
mysprite.hitpoints = 5
mysprite.move_nohard = true
mysprite.mx = 4
mysprite.my = 5
mysprite.nocontrol = true
mysprite.nodraw = false
mysprite.nohit = true
mysprite.notouch = false
mysprite.pframe = 3
mysprite.picfreeze = true
mysprite.pseq = 450
mysprite.que = -1000
mysprite.range = 10
mysprite.reverse = true
mysprite.size = 150
mysprite.sound = 20
mysprite.strength = 200
mysprite.target = myothersprite
mysprite.timing = 66
mysprite.touch_damage = -1
mysprite.kill = 1000
mysprite.freeze = true
mysprite.custom["key"] = 121
mysprite.custom.otherkey = 212
mysprite.active = false
-- used by some sprite brains
mysprite.action = 2
mysprite.attack_hit_sound = 44
mysprite.attack_hit_sound_speed = 96000
-- time in ms before the sprite runs attack()
mysprite.attack_wait = 10
-- base sequences
mysprite.base_attack = 450
mysprite.base_die = 200
mysprite.base_death = 200
mysprite.base_hit = 100
mysprite.base_idle = 10
mysprite.base_walk = 20
mysprite.brain = brain.REPEAT or 6
mysprite.brain_parm = 5
mysprite.brain_parm2 = 10
mysprite.defense = 100
mysprite.dir = direction.SOUTH or 2
mysprite.disabled = false
mysprite.distance = 100
mysprite.exp = 5000
mysprite.flying = true
mysprite.follow = myothersprite
mysprite.frame = 1
mysprite.seq = 100
mysprite.frame_delay = 33
mysprite.gold = 9000
mysprite.hard = true
mysprite.hitpoints = 5
mysprite.move_nohard = true
mysprite.mx = 4
mysprite.my = 5
mysprite.nocontrol = true
mysprite.nodraw = false
mysprite.nohit = true
mysprite.notouch = false
mysprite.pframe = 3
mysprite.picfreeze = true
mysprite.pseq = 450
mysprite.que = -1000
mysprite.range = 10
mysprite.reverse = true
mysprite.size = 150
mysprite.sound = 20
mysprite.strength = 200
mysprite.target = myothersprite
mysprite.timing = 66
mysprite.touch_damage = -1
mysprite.kill = 1000
mysprite.freeze = true
mysprite.custom["key"] = 121
mysprite.custom.otherkey = 212

And so on and so on. sniffs

There are also some aliases for the properties preceded by "no" in the affirmative instead:

mysprite.clip = false
-- sprite may be drawn over status bar
mysprite.control = false
mysprite.draw = false
-- sprite won't be drawn
mysprite.hit = false
-- can't be attacked
mysprite.touch = false
-- untouchable
mysprite.clip = false
-- sprite may be drawn over status bar
mysprite.control = false
mysprite.draw = false
-- sprite won't be drawn
mysprite.hit = false
-- can't be attacked
mysprite.touch = false
-- untouchable

Values can also be retrieved like so:

local x = mysprite.x
dink.debug(x)
-- will emit 105 to the debug output
local x = mysprite.x
dink.debug(x)
-- will emit 105 to the debug output

As is usual in the world of Dink, most things don't make sense, and several other sprite modifications are performed with methods, along with the usual fare such as for talking and moving:

mysprite:freeze()
mysprite:unfreeze()
mysprite:kill_wait()
mysprite:say("hi")
mysprite:say_stop("bye")
myothersprite:say_stop_npc("quack")
mysprite:move(direction.SOUTH, 100, true)
mysprite:move_stop(direction.NORTH, 100, false)
mysprite:draw_hard()
mysprite:hurt(5)
mysprite:kill_shadow()
mysprite:freeze()
mysprite:unfreeze()
mysprite:kill_wait()
mysprite:say("hi")
mysprite:say_stop("bye")
myothersprite:say_stop_npc("quack")
mysprite:move(direction.SOUTH, 100, true)
mysprite:move_stop(direction.NORTH, 100, false)
mysprite:draw_hard()
mysprite:hurt(5)
mysprite:kill_shadow()

These can also be used on the convenience variables of current_sprite and player with the latter having a few specific ones of its own:

player:set_speed(5)
player.get_speed
player.can_walk_off_screen = true
player.base_push = 500
player:set_speed(5)
player.get_speed
player.can_walk_off_screen = true
player.base_push = 500

Sound effects

Sound effects invoked with dink.playsound() will similarly return an object that has modifiable properties:

mysound = dink.playsound(44)
myothersound = dink.get_soundbank(global.soundjuggle)
-- retrieve a playsound invocation stored in a var
mysound.vol = 50
-- half volume
mysound.survive = true
mysound.pan = -10
-- to the left a little
mysound.hz = 22050
mysound.speed = 10
mysound.pause = true
mysound.looppoint = 4
mysound.repeating = true
mysound = dink.playsound(44)
myothersound = dink.get_soundbank(global.soundjuggle)
-- retrieve a playsound invocation stored in a var
mysound.vol = 50
-- half volume
mysound.survive = true
mysound.pan = -10
-- to the left a little
mysound.hz = 22050
mysound.speed = 10
mysound.pause = true
mysound.looppoint = 4
mysound.repeating = true

Along with some that only return a value

local count = mysound.loopcount
local position = mysound.pos
local distvol = mysound.overall_vol
-- for positional sounds
local count = mysound.loopcount
local position = mysound.pos
local distvol = mysound.overall_vol
-- for positional sounds

And similarly have some methods:

mysound:kill()
mysound:fade_vol(90, 2)
-- 90% volume over two seconds
mysound:fade_pan(50, 5)
-- half to the right over 5 seconds
mysound:fade_speed(10, 5)
-- 10% speed over 5 seconds
mysound:schedule_pause(5)
mysound:schedule_stop(8)
mysound:oscillate_pan(-10, 0, 3)
-- to the left and to centre over 3 seconds
mysound:oscillate_vol(80, 90, 5)
mysound:oscillate_speed(10, 100, 9)
mysound:kill()
mysound:fade_vol(90, 2)
-- 90% volume over two seconds
mysound:fade_pan(50, 5)
-- half to the right over 5 seconds
mysound:fade_speed(10, 5)
-- 10% speed over 5 seconds
mysound:schedule_pause(5)
mysound:schedule_stop(8)
mysound:oscillate_pan(-10, 0, 3)
-- to the left and to centre over 3 seconds
mysound:oscillate_vol(80, 90, 5)
mysound:oscillate_speed(10, 100, 9)

Please see below for the sound effect global object in 0.93.

Global variables

Creating and accessing global variables is accomplished using the "global" table.

Creation:

global.create("name", 0)
global.create("name", 0)

Changing/retrieving the value:

global.name = 5
local myname = global.name
global.name = 5
local myname = global.name

Choice menus

Creation is performed by making a new menu object, adding choices to it, and then showing it, with the result stored in a magical variable.

local menu = dink.create_choice_menu()
-- change params before showing it
menu.y = 100
menu.title = "Hello!"
menu.title_color = 5

-- add some choices
local what = menu:add_choice("What is going on?")
local where = menu:add_choice("Where am I?", global.location < 1)

local mychoice = menu:show()

if mychoice == what then
    current_sprite:say("I don't know")
end

if mychoice == where then
    current_sprite:say("It is a mystery")
end
local menu = dink.create_choice_menu()
-- change params before showing it
menu.y = 100
menu.title = "Hello!"
menu.title_color = 5

-- add some choices
local what = menu:add_choice("What is going on?")
local where = menu:add_choice("Where am I?", global.location < 1)

local mychoice = menu:show()

if mychoice == what then
    current_sprite:say("I don't know")
end

if mychoice == where then
    current_sprite:say("It is a mystery")
end

Editor sprite overrides

Accessed by creating an object out of a sprite that has a corresponding editor sprite, and then altering its parameters.

local myoverride = current_sprite.editor_sprite
myoverride.seq = 1
myoverride.frame = 10
myoverride.type = editor_type.KILL_RETURN_AFTER_ONE_MINUTE
local myoverride = current_sprite.editor_sprite
myoverride.seq = 1
myoverride.frame = 10
myoverride.type = editor_type.KILL_RETURN_AFTER_ONE_MINUTE

Helpers

Brain reference

These may be passed to a sprite's brain parameter when prefixed with "brain" instead of using the number:

      NONE = 0,
      PLAYER = 1,
      DUMB_SPRITE_BOUNCER = 2,
      DUCK = 3,
      PIG = 4,
      KILL_SEQ_DONE_LEAVE_LAST_FRAME = 5,
      REPEAT = 6,
      KILL_SEQ_DONE = 7,
      TEXT_SPRITE = 8,
      MONSTER_DIAGONAL = 9,
      MONSTER_NONDIAGONAL = 10,
      MISSILE = 11,
      BRAIN_PARM_SIZE_MATCH = 12,
      MOUSE = 13,
      BUTTON = 14,
      SHADOW = 15,
      SMART_PEOPLE = 16,
      MISSILE_KILL_SEQ_DONE = 17
      NONE = 0,
      PLAYER = 1,
      DUMB_SPRITE_BOUNCER = 2,
      DUCK = 3,
      PIG = 4,
      KILL_SEQ_DONE_LEAVE_LAST_FRAME = 5,
      REPEAT = 6,
      KILL_SEQ_DONE = 7,
      TEXT_SPRITE = 8,
      MONSTER_DIAGONAL = 9,
      MONSTER_NONDIAGONAL = 10,
      MISSILE = 11,
      BRAIN_PARM_SIZE_MATCH = 12,
      MOUSE = 13,
      BUTTON = 14,
      SHADOW = 15,
      SMART_PEOPLE = 16,
      MISSILE_KILL_SEQ_DONE = 17

To assign a duck brain for example, one would use

myothersprite.brain = brain.DUCK
mysprite.brain = 10
myothersprite.brain = brain.DUCK
mysprite.brain = 10

Directions

Same as above except for directions:

      SOUTH_WEST = 1,
      SOUTH = 2,
      SOUTH_EAST = 3,
      WEST = 4,
      EAST = 6,
      NORTH_WEST = 7,
      NORTH = 8,
      NORTH_EAST = 9
      SOUTH_WEST = 1,
      SOUTH = 2,
      SOUTH_EAST = 3,
      WEST = 4,
      EAST = 6,
      NORTH_WEST = 7,
      NORTH = 8,
      NORTH_EAST = 9

And used similarly as a substitution for the raw numbers:

myothersprite.dir = direction.EAST
mysprite.dir = 8
myothersprite.dir = direction.EAST
mysprite.dir = 8

Editor sprite override types

      KILL_COMPLETELY = 1,
      DRAW_WITHOUT_HARDNESS = 2,
      DRAW_BACKGROUND_WITHOUT_HARDNESS = 3,
      DRAW_WITH_HARDNESS = 4,
      DRAW_BACKGROUND_WITH_HARDNESS = 5,
      KILL_RETURN_AFTER_FIVE_MINUTES = 6,
      KILL_RETURN_AFTER_THREE_MINUTES = 7,
      KILL_RETURN_AFTER_ONE_MINUTE = 8
      KILL_COMPLETELY = 1,
      DRAW_WITHOUT_HARDNESS = 2,
      DRAW_BACKGROUND_WITHOUT_HARDNESS = 3,
      DRAW_WITH_HARDNESS = 4,
      DRAW_BACKGROUND_WITH_HARDNESS = 5,
      KILL_RETURN_AFTER_FIVE_MINUTES = 6,
      KILL_RETURN_AFTER_THREE_MINUTES = 7,
      KILL_RETURN_AFTER_ONE_MINUTE = 8
mysprite.editor_sprite.type = editor_type.DRAW_WITH_HARDNESS
mysprite.editor_sprite.type = editor_type.DRAW_WITH_HARDNESS

etc

SFX global object

In 0.93 one has the ability to change the default sound effects for usually unalterable things such as ducks, pigs, the inventory, and the status bar.

To change these, one must use the "sfx" global object:

sfx.mouse.sound = 55
sfx.mouse.hz = 44100
sfx.mouse.sound = 55
sfx.mouse.hz = 44100

This will change the sound that plays on the title screen when one clicks on something that isn't a button sprite to sound slot 55 at 44.1kHz. Along with "mouse" there is also:

sfx.attack
sfx.warp
sfx.duck
sfx.pig
sfx.hurt
sfx.attack
sfx.warp
sfx.duck
sfx.pig
sfx.hurt

Which all have a corresponding sound and hz property for you to alter to your liking. In the case of the pigs, the set sound determines the lowest-numbered sound effect that will be played, meaning if set to 50, sounds 50 through to 54 will be used.

Similarly in the case of the hurt sound, it will be set sound +1 for the second hurt sound.

There is also one more in the base sfx table:

sfx.sprite_hz
sfx.sprite_hz

This determines the playback rate for sprites assigned a sound in the editor or via scripts which is 22KHz by default.

Much like the hz parameter for dink.playsound, setting hz to zero will cause a sound to be played back at its native speed, except for ducks and pigs which alter the playback rate depending on the size of the duck or pig.

For a size 100 duck or pig, the sample rate calculation is as follows:

duck_hz - (size * 50)
duck_hz - (size * 50)

Therefore, to play back at 44.1kHz, one should specify a sample rate of 49100 to sfx.duck.hz. The pigs will also apply an 800Hz variance to sound playback.

As the status bar, inventory, and choice menus have multiple sound effects to play back, they are grouped under sub-objects like so:

sfx.status.stats.hz = 0
sfx.status.gold.sound = 9
-- for when experience is incremented
sfx.status.exp_tick.sound = 44
-- when experience counts up after new level
sfx.status.exp_count.hz = 0

sfx.inventory.enter.sound = 60
sfx.inventory.move.sound = 61
sfx.inventory.select.sound = 62
sfx.inventory.exit.sound = 63

sfx.choice_menu.scroll.sound = 99
sfx.choice_menu.select.sound = 100
sfx.status.stats.hz = 0
sfx.status.gold.sound = 9
-- for when experience is incremented
sfx.status.exp_tick.sound = 44
-- when experience counts up after new level
sfx.status.exp_count.hz = 0

sfx.inventory.enter.sound = 60
sfx.inventory.move.sound = 61
sfx.inventory.select.sound = 62
sfx.inventory.exit.sound = 63

sfx.choice_menu.scroll.sound = 99
sfx.choice_menu.select.sound = 100

There are also two functions:

sfx.play(sound, hz, random, sprite, loop)
sfx.halt_all()
sfx.play(sound, hz, random, sprite, loop)
sfx.halt_all()

Note that sfx.play() does not return a sound object and is purely for one-shotting with, and also doesn't work at all.

Music global object

An alternative interface to features usually handled by dink.playsound().

music.play("wanderer.ogg", 4000)
-- fade in ms param must be specified

music.play_mod_order(50)
-- tracker file pattern selection

music.pause()
music.resume()
music.stop()
music.fade_out(5000)

local muspos = music.position
local playingbool = music.is_playing()
local myduration = music.duration()
music.volume = 128
music.tempo = 0.5
-- only a few music formats support tempo
music.loop = true
-- equivalent to loopmidi
music.load_soundfont("coolfont.sf2")
-- loaded from root of dmoddir
print(music.title)
-- ID3 tag or filename usually
music.play("wanderer.ogg", 4000)
-- fade in ms param must be specified

music.play_mod_order(50)
-- tracker file pattern selection

music.pause()
music.resume()
music.stop()
music.fade_out(5000)

local muspos = music.position
local playingbool = music.is_playing()
local myduration = music.duration()
music.volume = 128
music.tempo = 0.5
-- only a few music formats support tempo
music.loop = true
-- equivalent to loopmidi
music.load_soundfont("coolfont.sf2")
-- loaded from root of dmoddir
print(music.title)
-- ID3 tag or filename usually

Second parameter to music.play() is a fade-in time, this is mandatory!

Timer global object

In 0.95, some of the inbuilt timer intervals may be overridden similar to the sound effects. All times are in milliseconds. These may alter things in unpredictable ways and should be approached with care.

timer.choice_arrows = 20
-- default is 100 for frame advance interval

timer.inventory = 100
-- default is 400 for the inventory flasher

timer.push = 100
-- default is 600 before the push animation plays

timer.notouch = 1000
-- touch damage cooldown, default 400

timer.water_tiles = 8000
-- upper limit for water tile change interval, default 2000

timer.bow_pull = 10
-- adjusts time between bow pulls

timer.bow = 100
-- adjusts bow frames. Changing this will require more frames

timer.fade_resume = 2000
-- amount of time before a fade-down resumes the script, default 1000

timer.fade_time = 900
-- amount of time for a fade to complete, default 400
timer.choice_arrows = 20
-- default is 100 for frame advance interval

timer.inventory = 100
-- default is 400 for the inventory flasher

timer.push = 100
-- default is 600 before the push animation plays

timer.notouch = 1000
-- touch damage cooldown, default 400

timer.water_tiles = 8000
-- upper limit for water tile change interval, default 2000

timer.bow_pull = 10
-- adjusts time between bow pulls

timer.bow = 100
-- adjusts bow frames. Changing this will require more frames

timer.fade_resume = 2000
-- amount of time before a fade-down resumes the script, default 1000

timer.fade_time = 900
-- amount of time for a fade to complete, default 400

Palette global object

In 0.95 and later, there are two functions for directly manipulating the colour palette. As usual with 8-bit graphics in Dink, it will only work as expected in 8-bit display mode.

palette.setindex(int index, int r, int g, int b)
palette.getindex(int index)

-- change index 2 to red
palette.setindex(2, 255, 0, 0)

-- retrieve a table containing RGB triplet
mycolour = palette.getindex(2)
print(mycolour[1])
-- will show 255, with indices 2 and 3 zero
palette.setindex(int index, int r, int g, int b)
palette.getindex(int index)

-- change index 2 to red
palette.setindex(2, 255, 0, 0)

-- retrieve a table containing RGB triplet
mycolour = palette.getindex(2)
print(mycolour[1])
-- will show 255, with indices 2 and 3 zero

There are also two functions related to changing the default colour of choice menu text.

palette.set_choice_options(0, 255, 0)
-- will change choices in the list to green
palette.set_choice_hover(255, 0, 0)
-- sets the hovered entry to red
palette.set_choice_options(0, 255, 0)
-- will change choices in the list to green
palette.set_choice_hover(255, 0, 0)
-- sets the hovered entry to red

Game global object

The "game" object contains a variety of variables and functions that deal with the inner workings of the engine.

The first is "experience_limit" which determines the value to next level up at. If changing this value, it is advisable to develop your own level-up system that doesn't rely on stopping the game, as the experience upper limit is checked each frame.

The second is "map_width" which determines how wide the map is. Setting map_width to 1 will cause the player to walk to the next screen when traversing either right or down. The default is 32. The third, "animate_tiles" determines if traditional fire and water tiles ranges will animate or not.

game.experience_limit = 10
-- trigger next level up after gaining ten experience
game.map_width = 2
-- make a long skinny map
game.animate_tiles = false
-- stop water from going up and down
game.dinkc_divequals_fix = true
-- allow for /= to work in DinkC scripts, or not
game.alttext_display = false
-- determine if the alternate text display for multiple colours in text should be enabled
game.sprite_multiscript = true
-- allows for sprites to have multiple scripts (experimental)
game.experience_limit = 10
-- trigger next level up after gaining ten experience
game.map_width = 2
-- make a long skinny map
game.animate_tiles = false
-- stop water from going up and down
game.dinkc_divequals_fix = true
-- allow for /= to work in DinkC scripts, or not
game.alttext_display = false
-- determine if the alternate text display for multiple colours in text should be enabled
game.sprite_multiscript = true
-- allows for sprites to have multiple scripts (experimental)

Plus some functions...

game.enable_hardened_mode()
-- make the debug tools and inbuilt cheat inaccessible
game.get_cheat()
-- retrieve a bool indicating if the user has accessed the inbuilt cheat
game.get_screen_hitmap()
-- returns a table containing the raw 600x400 collision values
game.is_controller_present()
-- returns a bool indicating if the player has a controller plugged in
game.getticks()
-- gets the engine uptime in milliseconds
game.enable_hardened_mode()
-- make the debug tools and inbuilt cheat inaccessible
game.get_cheat()
-- retrieve a bool indicating if the user has accessed the inbuilt cheat
game.get_screen_hitmap()
-- returns a table containing the raw 600x400 collision values
game.is_controller_present()
-- returns a bool indicating if the player has a controller plugged in
game.getticks()
-- gets the engine uptime in milliseconds